package mod

import (
	"gitee.com/go-errors/errors"
	"gitee.com/go-wena/wena/utils/pathx"
	"golang.org/x/mod/modfile"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

var ErrSkip = errors.New("skip")

type tx = modfile.File //hide it

type File struct {
	path string
	*tx
}

func New(path string, update func(modfile *File) error) error {
	modFile := &File{path: path, tx: &tx{Syntax: &modfile.FileSyntax{}}}
	if err := update(modFile); err != nil {
		if err == ErrSkip {
			return nil
		}
		return errors.Wrap(err)
	}

	return modFile.Save()
}

func Update(path string, update func(modFile *File) error) error {
	modFile, err := Open(path)
	if err != nil {
		return err
	}

	if err := update(modFile); err != nil {
		if err == ErrSkip {
			return nil
		}
		return errors.Wrap(err)
	}

	return modFile.Save()
}

func Open(path string) (*File, error) {
	if path != "go.mod" && !strings.HasSuffix(filepath.ToSlash(path), "/go.mod") {
		path = filepath.Join(path, "go.mod")
	}

	path = filepath.FromSlash(path)
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, errors.Wrap(err)
	}

	if mod, err := modfile.Parse(path, data, nil); err != nil {
		return nil, errors.Wrap(err)
	} else {
		return &File{tx: mod, path: path}, nil
	}
}

func (modFile *File) IsNil() bool {
	return modFile == nil || modFile.tx == nil
}

func (modFile *File) IsEmpty() bool {
	return modFile.IsNil() || modFile.Module.Mod.Path == ""
}

func (modFile *File) Save() error {
	if data, err := modFile.Format(); err != nil {
		return errors.Wrap(err)
	} else {
		return errors.Wrap(os.WriteFile(modFile.path, data, 0666))
	}
}

func (modFile *File) Package() string {
	if modFile.IsEmpty() {
		return ""
	}
	return modFile.Module.Mod.Path
}

func (modFile *File) Path() string {
	return modFile.path
}

func (modFile *File) Name() string {
	return GetNameFromPath(modFile.Package())
}

func GetNameFromPath(path string) (name string) {
	if path != "" {
		name = regexp.MustCompile(`/(v[\d]{1,3})$`).ReplaceAllString(strings.Trim(path, "/ "), "")
		if idx := strings.LastIndexByte(name, '/'); idx != -1 {
			name = name[idx+1:]
		}
	}
	return
}

func GetName(paths ...string) (dir string, name string) {
	dir = pathx.FindPathHasChild("go.mod", false, paths...)
	file, _ := Open(dir)
	if name = filepath.Base(file.Package()); name == "" {
		name = filepath.Base(dir)
	}
	if name == "" {
		name = "WenaApp"
	}
	return
}
